/* SelectionInsertionAssocation.m created by cfeder on Tue 08-Nov-1994 */

#import "SelectionInsertionAssociation.h"

@interface NSArray (_arrayWithObjectsNotInArray)
- (NSArray *)arrayWithObjectsNotInArray:other;
- (NSArray *)arrayOfSelectionIndexesForObjects:other;
@end

@implementation NSArray (_arrayWithObjectsNotInArray)
- (NSArray *)arrayWithObjectsNotInArray:other
{
    // Tremendously inefficient brain-dead implementation
    NSMutableArray *result = [NSMutableArray array];
    int i, count = [self count];
    for(i=0; i<count; i++) {
        id object = [self objectAtIndex:i];
        if ([other indexOfObjectIdenticalTo:object] == NSNotFound)
            [result addObject:object];
    }
    return result;
}

- (NSArray *)arrayOfSelectionIndexesForObjects:other
{
    // look up each of objects in other and return their indices in our array
    NSMutableArray *result = [NSMutableArray array];
    int i, count = [other count];
    for(i=0; i<count; i++) {
        id object = [other objectAtIndex:i];
        unsigned idx = [self indexOfObjectIdenticalTo:object];
        if ( idx != NSNotFound)
            [result addObject:[NSNumber numberWithUnsignedInt:idx]];
    }
    return result;
}
@end


// silly association to tell us about changes in the destination
@interface _SIMessager : EOAssociation
{
}
- initWithController:(EOController *)aController
        key: (NSString *)aKey destination: aDest;
@end

@implementation SelectionInsertionAssociation
// Our controller is the one we perform insert and delete operations on.
// Our destination is the controller that provides us with selected objects

- initWithController:(EOController *)aController
        key: (NSString *)aKey destination: aDest
{
    [super initWithController:aController key: aKey destination:aDest];

    // Our destination is a controller and we want to know when it changes.
    // We use another association to pass on relevant events from it.
    [destination addAssociation:[[[_SIMessager alloc] initWithController:destination key:nil destination:self] autorelease]];
    return self;
}

- (void)updateSelection
{
    NSArray *allObjects = [destination allObjects];
    NSArray *destinationObjects = [controller allObjects];
    
    [destination setSelectionIndexes:[allObjects arrayOfSelectionIndexesForObjects:destinationObjects]];
}

- (void)contentsDidChange
{
    if (!ignoreNotifications)
        [self updateSelection];
}

- (void)selectionDidChange
{
    if (!ignoreNotifications)
        [self updateSelection];
}

- (void)contentsDidChangeInController:(EOController *)aController
{
    // who cares
}

- (void)selectionDidChangeInController:(EOController *)aController
{
    // Our selection changed.  Perform relevant insertions and deletions in our
    // destination.
    NSArray *selectedObjects = [destination selectedObjects];
    NSArray *destinationObjects = [controller allObjects];
    NSArray *added = [selectedObjects arrayWithObjectsNotInArray:destinationObjects];
    NSArray *removed = [destinationObjects arrayWithObjectsNotInArray:selectedObjects];
    int i, count;

    ignoreNotifications = YES;

    // We do deletions before insertions so that the right thing happens for
    // to-one detail relationships (insert before delete would, for a moment,
    // put two objects at the destination of a to-one)
    for(i=0,count=[removed count]; i<count; i++) {
        id object = [removed objectAtIndex:i];
        unsigned idx = [[controller allObjects] indexOfObjectIdenticalTo:object];
        if (idx != NSNotFound)
            [controller deleteObjectAtIndex:idx];
    }

    for(i=0,count=[added count]; i<count; i++) {
        id object = [added objectAtIndex:i];
        [controller insertObject:object atIndex:0];
    }

    ignoreNotifications = NO;
}
@end


@implementation _SIMessager
- initWithController:(EOController *)aController
        key: (NSString *)aKey destination: aDest
    // destination is object we forward association messages to
    // key is not used
{
    return [super initWithController:aController key: aKey destination:aDest];
}

- (void)selectionDidChange
{
    [destination selectionDidChangeInController:[self controller]];
}
- (void)contentsDidChange
{
    [destination contentsDidChangeInController:[self controller]];
}
@end

